﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Red.Controls.Converters;
using Red.Controls.Utils;
using Red.Core.Extensions;
using Red.Core.GoogleAnalytics;

namespace Red.Controls.Shell
{
    /// <summary>
    ///     The base class for main windows for all application using Visual Framework
    /// </summary>
    public class RedMainWindow : RedWindow
    {
        #region Data

        private readonly DispatcherTimer _settingsTimer = new DispatcherTimer();
        private static string _lastTooltipText;
        private RedSettings _settings;

        #endregion Data

        #region Properties

        /// <summary>
        ///     Initialized by the framework, which also takes care of saving it.
        /// </summary>
        public RedSettings Settings { get { return _settings; } set { _settings = value; InitializeSettings(); } }

        /// <summary>
        /// Indicates if InitializeSettings() has been called
        /// </summary>
        public bool SettingsInitialized { get; private set; }

        /// <summary>
        ///     Set this to true to allow the framework to periodically save Settings to disk.
        /// </summary>
        public bool StoreUserSettingsPeriodically { get; set; }

        #endregion Properties

        #region C-tor

        public RedMainWindow()
        {
            WindowTransitionsEnabled = false;
            CommandBindings.Add( new CommandBinding( ApplicationCommands.Close, Close_Executed ) );
            CommandBindings.Add( new CommandBinding( UICommands.ResetLayout, ResetLayout_Executed, ResetLayout_CanExecute ) );
            CommandBindings.Add( new CommandBinding( UICommands.SaveLayout, SaveLayout_Executed, SaveLayout_CanExecute ) );
            CommandBindings.Add( new CommandBinding( UICommands.LoadLayout, LoadLayout_Executed, LoadLayout_CanExecute ) );
            CommandBindings.Add( new CommandBinding( UICommands.ShowHelp, ShowHelp_Execute ) );

            _settingsTimer.Tick += SettingsTimer_Tick;
            _settingsTimer.Interval = new TimeSpan( 0, 0, 1 );
            _settingsTimer.Start();
        }

        static RedMainWindow()
        {
            // Intercept the active tooltip text by plug-in to TooltipService guts
            ToolTipService.PlacementTargetProperty.OverrideMetadata( typeof( FrameworkElement ), new FrameworkPropertyMetadata( ToolTipPlacementTargetPropertyChanged ) );
        }

        #endregion C-tor

        #region Public methods

        /// <summary>
        ///     Shows the editor-wide error box and (optionally) closes the editor window. Showing is enqueued (not blocking).
        /// </summary>
        /// <param name="message">Message to display</param>
        /// <param name="fatal">If true, closes the window afterwards</param>
        public async Task ShowErrorAsync( string message, bool fatal = false )
        {
            await RedMessageBox.ShowAsync( this, RedDialogType.Ok, message );
            if ( fatal )
            {
                Close();
            }
        }

        #endregion Public methods

        #region Protected interface

        protected virtual void WriteToSettings( RedSettings settings ) { }

        protected virtual void LoadFromSettings( RedSettings settings ) { }

        protected virtual GoogleAnalyticsContext GetGoogleAnalyticsContext()
        {
            return GoogleAnalyticsContext.Empty;
        }

        protected virtual IntPtr? GetPreviewHandle()
        {
            return GetFrame()?.PreviewPanel?.Handle;
        }

        #endregion Protected interface

        #region Protected methods

        /// <summary>
        ///     Notifies the framework that user settings has been modified. Causes saving and re-applying.
        /// </summary>
        protected void SettingsModified()
        {
            // Don't send any events to google analytics when reloading settings
            GoogleAnalyticsService.Instance.DisableAnalytics = true;

            Settings.Save();
            LoadFromSettings( Settings ); // re-sync

            GoogleAnalyticsService.Instance.DisableAnalytics = false;
        }

        protected void ReloadDefaultSettings()
        {
            Settings.Reset();

            // We specifically want the default values, *not* stale settings from the previous value
            Settings.UpgradeRequired = false;

            SettingsModified();
        }

        protected Frame GetFrame()
        {
            return Helpers.FindFirstVisualChild<Frame>( this );
        }

        #endregion Protected methods

        #region Private methods

        private void SaveLayout( bool user )
        {
            Debug.Assert( Settings != null );
            Frame frame = GetFrame();
            if ( frame != null )
            {
                if ( user )
                {
                    Settings.UserWindowLayout = frame.GetLayoutString();
                }
                else
                {
                    Settings.WindowLayout = frame.GetLayoutString();
                }
            }
        }

        private void ApplyLayout( bool user )
        {
            Debug.Assert( Settings != null );
            Frame frame = GetFrame();
            if ( frame != null )
            {
                if ( !frame.ApplyLayoutString( user ? Settings.UserWindowLayout : Settings.WindowLayout ) )
                {
                    ShowErrorAsync( "Error loading layout" ).DoNotAwait();
                }
            }
        }

        private void LoadLayout()
        {
            if ( SettingsInitialized )
            {
                // Load window's position FIRST as the undocked drawers' layout is stored relative to it
                WindowPositionSaver.Load( this, Settings );
                ApplyLayout( false );
            }
        }

        /// <summary>
        /// Load settings from previous version and pass them through to the application at large
        /// </summary>
        private void InitializeSettings()
        {
            if ( Settings != null )
            {
                // If this is true, the defaults have been loaded and we should attempt to
                // restore settings from a previous version
                if ( Settings.UpgradeRequired )
                {
                    Settings.Upgrade();
                    Settings.UpgradeRequired = false;
                }

                LoadFromSettings( Settings ); // let the derived-class to load its stuff

                SettingsInitialized = true;
            }
        }

        #endregion Private methods

        #region Event handlers

        void SettingsTimer_Tick( object sender, EventArgs e )
        {
            if ( IsLoaded && Settings != null )
            {
                if ( StoreUserSettingsPeriodically )
                {
                    WriteToSettings( Settings );
                }
                WindowPositionSaver.Save( this, Settings );
                SaveLayout( false );
                Settings.Save();
            }
        }

        private void Close_Executed( object sender, EventArgs e )
        {
            Close();
        }

        private void ResetLayout_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            Debug.Assert( GetFrame() != null );

            if ( !RedMessageBox.Show( this, RedDialogType.YesNo, "Reset layout?" ) )
            {
                return;
            }

            GetFrame().ResetLayout();
        }

        private void ResetLayout_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ( GetFrame() != null );
        }

        private void LoadLayout_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            if ( !RedMessageBox.Show( this, RedDialogType.YesNo, "Load user layout?" ) )
            {
                return;
            }

            ApplyLayout( true );
        }

        private void LoadLayout_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ( GetFrame() != null && Settings != null && !string.IsNullOrEmpty( Settings.UserWindowLayout ) );
        }

        private void SaveLayout_Executed( object sender, ExecutedRoutedEventArgs e )
        {
            if ( !string.IsNullOrEmpty( Settings.UserWindowLayout ) )
            {
                if ( !RedMessageBox.Show( this, RedDialogType.YesNo, "Overwrite user layout?" ) )
                {
                    return;
                }
            }

            SaveLayout( true );
        }

        private void SaveLayout_CanExecute( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = ( GetFrame() != null && Settings != null );
        }

        protected override void OnClosing( System.ComponentModel.CancelEventArgs e )
        {
            base.OnClosing( e );

            if ( Settings != null )
            {
                WriteToSettings( Settings ); // let the derived-class to save its stuff
                // NOTE: MetroWindow's automatic position saving is not multi-monitor-proof. This one is.
                WindowPositionSaver.Save( this, Settings );
                SaveLayout( false );
                Settings.Save();
            }

            GoogleAnalyticsService.Instance.SendEvent( "Closed", "Application" );
            GoogleAnalyticsService.Instance.EndSession();
        }

        protected override void OnSourceInitialized( EventArgs e )
        {
            base.OnSourceInitialized( e );

            LoadLayout();

            GoogleAnalyticsService.Instance.Init( GetGoogleAnalyticsContext() );
            InitAnalyticsSystemMetrics();
            GoogleAnalyticsService.Instance.BeginSession();
            GoogleAnalyticsService.Instance.SetScreen( "MainWindow" );
            GoogleAnalyticsService.Instance.SendEvent( "Started", "Application" );
        }

        private void InitAnalyticsSystemMetrics()
        {
            //Calculate the screen resolution
            PresentationSource MainWindowPresentationSource = PresentationSource.FromVisual( this );
            Matrix m = MainWindowPresentationSource.CompositionTarget.TransformToDevice;
            double dpiWidthFactor = m.M11;
            double dpiHeightFactor = m.M22;
            GoogleAnalyticsService.Instance.ScreenHeight = (int)( SystemParameters.PrimaryScreenHeight * dpiHeightFactor );
            GoogleAnalyticsService.Instance.ScreenWidth = (int)( SystemParameters.PrimaryScreenWidth * dpiWidthFactor );

            // Colour depth
            GoogleAnalyticsService.Instance.ScreenDepth = System.Windows.Forms.Screen.PrimaryScreen.BitsPerPixel;

            // locale
            GoogleAnalyticsService.Instance.Locale = CultureInfo.CurrentCulture.IetfLanguageTag;
        }

        static void ToolTipPlacementTargetPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var toolTip = d as ToolTip;
            if( toolTip != null )
            {
                if ( e.NewValue != null )
                {
                    _lastTooltipText = toolTip.Content as string;
                    var wnd = GetWindow( toolTip.PlacementTarget );
                    if ( wnd != null )
                    {
                        wnd.Activate(); // activate so F1 works
                    }
                }
                else
                {
                    _lastTooltipText = null;
                }
            }
        }

        private async void ShowHelp_Execute( object sender, ExecutedRoutedEventArgs e )
        {
            if ( _lastTooltipText != null )
            {
                string link = ToolTipTextConverter.SectionText( _lastTooltipText, ToolTipSection.Link );

                if ( !string.IsNullOrEmpty( link ) )
                {
                    try
                    {
                        Process.Start( link );
                    }
                    catch ( Exception ex )
                    {
                        await ShowErrorAsync( ex.Message );
                    }
                }
            }
        }

        #endregion Event handlers
    }
}
